﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace IRCClient
{
    /// <summary>
    /// Klasi fyrir skilaboð.
    /// </summary>
	class Message
	{
        //Regular expression notað til að match-a ýmislegt sem við notum í klasanum, t.d ctcp, replycode ofl.
		private static Regex regexServer = new Regex(@"^([a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)(\.[a-zA-Z0-9]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)*(\.[a-zA-Z]{1}([a-zA-Z0-9\-]*[a-zA-Z0-9])*)\.?$");
		private static Regex _ReplyCodeRegex = new Regex("^:[^ ]+? ([0-9]{3}) .+$", RegexOptions.Compiled);
		private static Regex _PingRegex = new Regex("^PING :.*", RegexOptions.Compiled);
		private static Regex _ErrorRegex = new Regex("^ERROR :.*", RegexOptions.Compiled);
		private static Regex _ActionRegex = new Regex("^:.*? PRIVMSG (.).* :" + "\x1" + "ACTION .*" + "\x1" + "$", RegexOptions.Compiled);
		private static Regex _CtcpRequestRegex = new Regex("^:.*? PRIVMSG .* :" + "\x1" + ".*" + "\x1" + "$", RegexOptions.Compiled);
		private static Regex _MessageRegex = new Regex("^:.*? PRIVMSG (.).* :.*$", RegexOptions.Compiled);
		private static Regex _CtcpReplyRegex = new Regex("^:.*? NOTICE .* :" + "\x1" + ".*" + "\x1" + "$", RegexOptions.Compiled);
		private static Regex _NoticeRegex = new Regex("^:.*? NOTICE (.).* :.*$", RegexOptions.Compiled);
		private static Regex _InviteRegex = new Regex("^:.*? INVITE .* .*$", RegexOptions.Compiled);
		private static Regex _JoinRegex = new Regex("^:.*? JOIN .*$", RegexOptions.Compiled);
		private static Regex _TopicRegex = new Regex("^:.*? TOPIC .* :.*$", RegexOptions.Compiled);
		private static Regex _NickRegex = new Regex("^:.*? NICK .*$", RegexOptions.Compiled);
		private static Regex _KickRegex = new Regex("^:.*? KICK .* .*$", RegexOptions.Compiled);
		private static Regex _PartRegex = new Regex("^:.*? PART .*$", RegexOptions.Compiled);
		private static Regex _ModeRegex = new Regex("^:.*? MODE (.*) .*$", RegexOptions.Compiled);
		private static Regex _QuitRegex = new Regex("^:.*? QUIT :.*$", RegexOptions.Compiled);
        
        public string rawMessage;
		public string source;
		public string sourceNick;
		public string target;
		public string message;
		public MessageType type;
		public ReplyCode replyCode;

		public Message()
		{
		}
			
		/// <summary>
		/// Smiður sem upphafsstillir meðlimabreyturnar.
		/// </summary>
		/// <param name="rawMessage">skilaboðin hrá</param>
		/// <param name="source">Hvaðan skilaboðin koma</param>
		/// <param name="target">Hvert skilaboð eiga að fara</param>
		/// <param name="message">skilaboðin</param>
		/// <param name="type">týpan á skilaboðinu</param>
		public Message(string rawMessage, string source, string sourceNick, string target, string message, MessageType type, ReplyCode replyCode)
		{
			this.rawMessage = rawMessage;
			this.source = source;
			this.sourceNick = sourceNick;
			this.target = target;
			this.message = message;
			this.type = type;
			this.replyCode = replyCode;
		}

        /// <summary>
        /// Fall til að parsa niður skilaboð eftir source,target,skilaboði ofl skv meðlimabreytum fallsins.
        /// </summary>
        /// <param name="rawLine">Tekur inn hráa línu</param>
        /// <returns>Skilar tilbúnu skilaboði sem búið er að parsa niður.</returns>
		public Message Parse(string rawLine)
		{
			string[] rawLineArr = rawLine.Split(' ');
			string line = "";
			string[] lineArr;
			string messageCode = "";
			string source = "";
			string sourceNick = "";
			string target = "";
			string message = "";
			ReplyCode replyCode = ReplyCode.Unknown;
			MessageType type = MessageType.Unknown;

            //Ef fyrsta stakið í rawLine strengum er tvípunktur.
			if (rawLine[0] == ':')
			{
				//Láta line taka gildið á strengnum án fyrsta staksins.
				line = rawLine.Substring(1);
			}
            //Annars tekur line gildið á öllu rawLine.
			else
			{
				line = rawLine;
			}

            //Splitta line upp á bilum og geyma í lineArr.
			lineArr = line.Split(' ');

            //Source er fyrsta gildið í line array-inu.
			source = lineArr[0];

            //skilaboða code-ið er annað stakið.
			messageCode = lineArr[1];

			// Ef það er ekki PING frá server eða álíka
			if (lineArr.Length > 2)
				target = lineArr[2];

			try
			{
                //Parsa skilaboða code-ið úr streng í int og type-casta því í ReplyCode.
				replyCode = (ReplyCode)int.Parse(messageCode);
			}
			catch (FormatException)
			{
                //Ef code-ið er ekki þekkt.
				replyCode = ReplyCode.Unknown;
			}

            //Staðsetning á tvípunkti í línu.
			int colonpos = line.IndexOf(" :");
			int exclamationPos = source.IndexOf("!");

            //Ef það er tvípunktur í línunni.
            if (colonpos != -1)
            {
                //Skilaboðum gefið gildið í línunni á eftir tvípunkt og tveimur bilum.
                message = line.Substring(colonpos + 2);
            }
			if (exclamationPos != -1)
			{
				sourceNick = source.Substring(0, exclamationPos);
			}

            //Kalla á fallið getMessageType, sem skilar okkur týpunni á skilaboðinu.
			type = getMessageType(rawLine);

            //Fallið skilar nýju skilaboði.
			return new Message(rawLine, source, sourceNick, target, message, type, replyCode);
		}

        /// <summary>
        /// Fall sem skilar týpu á skilaboði.
        /// </summary>
        /// <param name="rawLine">Tekur inn hráa línu.</param>
        /// <returns>Skilar ákveðnum MessageType, fer eftir skilaboðinu sem kemur inn</returns>
		public MessageType getMessageType(string rawLine)
		{
            //Athuga hvort hráa línan passaði við ákveðið regular expression reply code (sem er skilgreint efst í klasanum)
			Match found = _ReplyCodeRegex.Match(rawLine);
            //Ef hún passaði við ákveðið regular expression.
			if (found.Success)
			{
                //Skilar gildinu á þeim sem pössuðu við þessa regular expression.
				string code = found.Groups[1].Value;

                //Parsa skilaboða code-ið úr streng í int og type-casta því í ReplyCode.
				this.replyCode = (ReplyCode)int.Parse(code);

                //Switch/case til að skila Skilaboðatýpunni sem skilaboð frá server.
                //Annars skilaboðatýpunni sem error.
				switch (replyCode)
				{
					case ReplyCode.Motd:
					case ReplyCode.NamesReply:
					case ReplyCode.EndOfNames:
                    case ReplyCode.Topic:
                    case ReplyCode.NoTopic:
						return MessageType.ServerMessage;
					default:
						{
							if ((int)replyCode >= 400 && (int)replyCode <= 599)
								return MessageType.Error;
							break;
						}
				}
			}

            //Athuga hvort skilaboðatýpan sé Ping.
			found = _PingRegex.Match(rawLine);
			if (found.Success)
			{
				return MessageType.Ping;
			}

            //Athuga hvort skilaboðatýpan sé ctcp eða dcc request.
			found = _CtcpRequestRegex.Match(rawLine);
			if (found.Success)
			{
				if (rawLine.ToLower().Contains(Constants.CtcpChar + "dcc send"))
					return MessageType.DCCRequest;

				return MessageType.CtcpMessage;
			}

            //Athuga hvort skilaboðatýpan sé PrivateMessage.
			found = _MessageRegex.Match(rawLine);
			if (found.Success)
			{
				//string s = rawLine.Substring(rawLine.IndexOf(' '), rawLine.IndexOf(" :"));
				//int hashPos = s.IndexOf('#');
				//if (hashPos != -1)
					//return MessageType.ChannelMessage;

				return MessageType.PrivateMessage;
			}

            //Athuga hvort skilaboðatýpan sé Notice
			found = _NoticeRegex.Match(rawLine);
			if (found.Success)
			{
				return MessageType.Notice;
			}

			found = _JoinRegex.Match(rawLine);
			if (found.Success)
			{
				return MessageType.Join;
			}

			found = _QuitRegex.Match(rawLine);
			if (found.Success)
			{
				return MessageType.Quit;
			}

			found = _PartRegex.Match(rawLine);
			if (found.Success)
			{
				return MessageType.Part;
			}

            //Annars ef ekkert að ofanverðu var skilað þá er message týpan óskilgreind.
			return MessageType.Unknown;
		}
	}

    /// <summary>
    /// Enum fyrir Skilaboða týpu sem við skilgreinum.
    /// </summary>
	enum MessageType
	{
		ServerMessage,
		Notice,
		PrivateMessage,
		ChannelMessage,
		CtcpMessage,
		Ping,
		Error,
		DCCRequest,
		Join,
		Quit,
		Part,
		Unknown
	}
}
